#ifndef GDB_DEBUGGER_COMMANDS_H
#define GDB_DEBUGGER_COMMANDS_H

#include <map>
#include <wx/string.h>
#include <wx/regex.h>
#include <wx/tipwin.h>
#include <wx/tokenzr.h>
#include <M_Code_editor.h>
#include <M_Code_debugger_interfaces.h>
#include <configmanager.h>
#include <globals.h>
#include <manager.h>
#include <editormanager.h>
#include <infowindow.h>
#include <logmanager.h>
#include <macrosmanager.h>
#include "debugger_defs.h"
#include "Debugger.h"
#include "gdb_driver.h"
#include "remotedebugging.h"
#include "parsewatchvalue.h"

namespace {
  template <typename T>
  T wxStrHexTo( const wxString &str ) {
    T ret = 0;
    std::size_t count = 0;
    std::size_t pos = 0;
    if( str[pos] == _T( '0' ) ) {
      ++pos;
      if( str[pos] == _T( 'x' ) || str[pos] == _T( 'X' ) ) {
        ++pos;
      }
      while( str[pos] == _T( '0' ) ) {
        ++pos;
      }
    }
    while( count < sizeof( T ) * 2 ) {
      switch( str[pos] ) {
        case _T( '0' ):
        case _T( '1' ):
        case _T( '2' ):
        case _T( '3' ):
        case _T( '4' ):
        case _T( '5' ):
        case _T( '6' ):
        case _T( '7' ):
        case _T( '8' ):
        case _T( '9' ):
          ret <<= 4;
          ret |= str[pos] - _T( '0' );
          ++count;
          break;
        case _T( 'a' ):
        case _T( 'b' ):
        case _T( 'c' ):
        case _T( 'd' ):
        case _T( 'e' ):
        case _T( 'f' ):
          ret <<= 4;
          ret |= str[pos] - _T( 'a' ) + 10;
          ++count;
          break;
        case _T( 'A' ):
        case _T( 'B' ):
        case _T( 'C' ):
        case _T( 'D' ):
        case _T( 'E' ):
        case _T( 'F' ):
          ret <<= 4;
          ret |= str[pos] - _T( 'A' ) + 10;
          ++count;
          break;
        default:
          return ret;
      }
      ++pos;
    }
    return ret;
  }
}

static wxRegEx reBT0( _T( "#([0-9]+)[ \t]+(.+)[ \t]at[ \t](.+):([0-9]+)" ) );
static wxRegEx reBT1( _T( "#([0-9]+)[ \t]+0x([A-Fa-f0-9]+)[ \t]+in[ \t]+(.+)[ \t]+(\\([^)]*\\))[ \t]" ) );
static wxRegEx reBTX( _T( "#([0-9]+)[ \t]+0x([A-Fa-f0-9]+)[ \t]+in[ \t]+([^(]+)[ \t]*(\\([^)]*\\)[ \t]*\\([^)]*\\))" ) );
static wxRegEx reBT2( _T( "\\)[ \t]+[atfrom]+[ \t]+(.*):([0-9]+)" ) );
static wxRegEx reBT3( _T( "\\)[ \t]+[atfrom]+[ \t]+(.*)" ) );
static wxRegEx reBT4( _T( "#([0-9]+)[ \\t]+(.+)[ \\t]in[ \\t](.+)" ) );
static wxRegEx reBreakpoint( _T( "Breakpoint ([0-9]+) at (0x[0-9A-Fa-f]+)" ) );
static wxRegEx rePendingBreakpoint( _T( "Breakpoint ([0-9]+)[ \t]\\(\"(.+):([0-9]+)(\"?)\\)[ \t]pending\\." ) );
static wxRegEx reHWBreakpoint( _T( "Hardware assisted breakpoint ([0-9]+) at (0x[0-9A-Fa-f]+)" ) );
static wxRegEx reDataBreakpoint( _T( "Hardware watchpoint ([0-9]+):.*" ) );
static wxRegEx reTemporaryBreakpoint( _T( "^[Tt]emporary[ \t]breakpoint[ \t]([0-9]+)[ \t]at.*" ) );
static wxRegEx reRegisters( _T( "([A-z0-9]+)[ \t]+(0x[0-9A-Fa-f]+)[ \t]+(.*)" ) );
static wxRegEx reDisassembly( _T( "(0x[0-9A-Za-z]+)[ \t]+<.*>:[ \t]+(.*)" ) );
static wxRegEx reDisassemblySource( _T( "([0-9]+)[ \t](.*)" ) );
static wxRegEx reDisassemblyInit( _T( "^[ \t]*Stack level [0-9]+, frame at (0x[A-Fa-f0-9]+):" ) );
static wxRegEx reDisassemblyInitSymbol( _T( "[ \t]*[er]ip[ \t]+=[ \t]+0x[0-9a-f]+[ \t]+in[ \t]+(.+)\\((.+):([0-9]+)\\);" ) );
static wxRegEx reDisassemblyInitFunc( _T( "eip = (0x[A-Fa-f0-9]+) in ([^;]*)" ) );
static wxRegEx reDisassemblyInitFuncOR32( _T( "PC = (0x[A-Fa-f0-9]+) in ([^;]*)" ) );
static wxRegEx reDisassemblyCurPC( _T( "=>[ \t]+(0x[A-Fa-f0-9]+)" ) );
static wxRegEx reInfoProgramThread( _T( "\\(LWP[ \t]([0-9]+)\\)" ) );
static wxRegEx reInfoProgramProcess( _T( "child process ([0-9]+)" ) );
static wxRegEx reInfoThreads( _T( "(\\**)[ \t]*([0-9]+)[ \t](.*)" ) );
static wxRegEx reGenericHexAddress( _T( "(0x[A-Fa-f0-9]+)" ) );
static wxRegEx reExamineMemoryLine( _T( "[ \t]*(0x[0-9a-f]+)[ \t]<.+>:[ \t]+(.+)" ) );
static wxRegEx reStepI( _T( "(((.*)[a-zA-Z]:)?.*)?:(\\d+):(middle|beg):(.*)" ), wxRE_ADVANCED );
static wxRegEx reStepI2( _T( "\\A(0x[A-Fa-f0-9]+)\\s+(\\d+)\\s+in (.*)" ), wxRE_ADVANCED );
static wxRegEx reStepI3( _T( "^(0x[A-Fa-f0-9]+) in (.*)? from (.*)" ) );
static wxRegEx reStepI4( _T( "^(0x[A-Fa-f0-9]+) in (.*)? at (.*)" ) );
static wxRegEx reNextI( _T( "\x1a\x1a(([a-zA-Z]:)?.*?):([0-9]*):([0-9]*):(middle|beg):(.*)" ), wxRE_ADVANCED );

class GdbCmd_AddSourceDir : public DebuggerCmd {
  public:
    GdbCmd_AddSourceDir( DebuggerDriver* driver, const wxString& dir )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "directory " ) << dir;
    }
    void ParseOutput( const wxString& output ) {
      if( output.StartsWith( _T( "Warning: " ) ) ) {
        m_pDriver->Log( output.BeforeFirst( _T( '\n' ) ) );
      }
    }
};


class GdbCmd_SetDebuggee : public DebuggerCmd {
  public:
    GdbCmd_SetDebuggee( DebuggerDriver* driver, const wxString& file )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "file " ) << file;
    }
    void ParseOutput( const wxString& output ) {
      m_pDriver->Log( output.BeforeFirst( _T( '\n' ) ) );
    }
};


class GdbCmd_AddSymbolFile : public DebuggerCmd {
  public:
    GdbCmd_AddSymbolFile( DebuggerDriver* driver, const wxString& file )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "add-symbol-file " ) << file;
    }
    void ParseOutput( const wxString& output ) {
      m_pDriver->Log( output.AfterFirst( _T( '\n' ) ).BeforeLast( _T( '\n' ) ) );
    }
};


class GdbCmd_SetArguments : public DebuggerCmd {
  public:
    GdbCmd_SetArguments( DebuggerDriver* driver, const wxString& args )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "set args " ) << args;
    }
    void ParseOutput( M_Code_unused const wxString& output ) {
    }
};


class GdbCmd_AttachToProcess : public DebuggerCmd {
  public:
    GdbCmd_AttachToProcess( DebuggerDriver* driver, int pid )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "attach " ) << F( _T( "%d" ), pid );
      m_pDriver->Log( F( _T( "Attaching to program with pid: %d" ), pid ) );
    }
    void ParseOutput( const wxString& output ) {
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        if( lines[i].StartsWith( _T( "Attaching" ) ) ) {
          m_pDriver->Log( lines[i] );
        } else if( lines[i].StartsWith( _T( "Can't " ) )
                   || lines[i].StartsWith( _T( "Could not attach to process" ) )
                   || lines[i].StartsWith( _T( "ptrace: No such process" ) ) ) {
          m_pDriver->Log( _T( "Attaching failed: " ) + lines[i] );
          m_pDriver->QueueCommand( new DebuggerCmd( m_pDriver, _T( "quit" ) ) );
        }
      }
    }

    bool IsContinueCommand() const override { return true; }
};


class GdbCmd_Detach : public DebuggerCmd {
  public:
    GdbCmd_Detach( DebuggerDriver* driver )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "detach" );
    }
    void ParseOutput( const wxString& output ) {
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        if( lines[i].StartsWith( _T( "Detaching" ) ) ) {
          m_pDriver->Log( lines[i] );
        }
      }
    }
};


class GdbCmd_AddBreakpointCondition : public DebuggerCmd {
    cb::shared_ptr<DebuggerBreakpoint> m_BP;
  public:
    GdbCmd_AddBreakpointCondition( DebuggerDriver* driver, cb::shared_ptr<DebuggerBreakpoint> bp )
      : DebuggerCmd( driver ),
        m_BP( bp ) {
      m_Cmd << _T( "condition " ) << F( _T( "%ld" ), ( int ) m_BP->index );
      if( m_BP->useCondition ) {
        m_Cmd << _T( " " ) << m_BP->condition;
      }
    }
    void ParseOutput( const wxString& output ) {
      if( output.StartsWith( _T( "No symbol " ) ) ) {
        wxString s = F( _T( "While setting up custom conditions for breakpoint %ld (%s, line %d),\n"
                            "the debugger responded with the following error:\n"
                            "\nError: %s\n\n"
                            "Do you want to make this an un-conditional breakpoint?" ),
                        m_BP->index,
                        m_BP->filename.c_str(),
                        m_BP->line + 1,
                        output.c_str() );
        if( msging( s, _T( "警告" ), wxICON_WARNING | wxYES_NO ) == wxID_YES ) {
          m_BP->useCondition = false;
          m_pDriver->QueueCommand( new GdbCmd_AddBreakpointCondition( m_pDriver, m_BP ), DebuggerDriver::High );
        } else if( m_BP->alreadySet ) {
          m_pDriver->RemoveBreakpoint( m_BP );
          ( ( M_Code_Editor* )Manager::Get()->GetEditorManager()->GetActiveEditor() )->SetDebugLine( -1 );
          m_pDriver->Continue();
        }
      }
    }
};


class GdbCmd_AddBreakpoint : public DebuggerCmd {
    cb::shared_ptr<DebuggerBreakpoint> m_BP;
  public:
    GdbCmd_AddBreakpoint( DebuggerDriver* driver, cb::shared_ptr<DebuggerBreakpoint> bp )
      : DebuggerCmd( driver ),
        m_BP( bp ) {
      m_BP->index = -1;
      if( m_BP->enabled ) {
        if( m_BP->type == DebuggerBreakpoint::bptCode ) {
          wxString out = m_BP->filename;
          if( !m_BP->temporary ) {
            m_Cmd << _T( "break " );
          } else
          { m_Cmd << _T( "tbreak " ); }
          m_Cmd << _T( '"' ) << out << _T( ":" ) << F( _T( "%d" ), m_BP->line ) << _T( '"' );
        } else if( m_BP->type == DebuggerBreakpoint::bptData ) {
          if( m_BP->breakOnRead && m_BP->breakOnWrite ) {
            m_Cmd << _T( "awatch " );
          } else if( m_BP->breakOnRead ) {
            m_Cmd << _T( "rwatch " );
          } else
          { m_Cmd << _T( "watch " ); }
          m_Cmd << m_BP->breakAddress;
        } else
        { m_Cmd << _T( "rbreak " ) << m_BP->func; }
        m_BP->alreadySet = true;
      }
    }
    void ParseOutput( const wxString& output ) {
      if( reBreakpoint.Matches( output ) ) {
        if( !m_BP->func.IsEmpty() ) {
          m_pDriver->Log( _T( "GDB workaround for constructor/destructor breakpoints activated." ) );
        }
        reBreakpoint.GetMatch( output, 1 ).ToLong( &m_BP->index );
        reBreakpoint.GetMatch( output, 2 ).ToULong( &m_BP->address, 16 );
        if( m_BP->useCondition && !m_BP->condition.IsEmpty() ) {
          m_pDriver->QueueCommand( new GdbCmd_AddBreakpointCondition( m_pDriver, m_BP ), DebuggerDriver::High );
        }
        if( m_BP->useIgnoreCount && m_BP->ignoreCount > 0 ) {
          wxString cmd;
          cmd << _T( "ignore " ) << F( _T( "%d" ), ( int ) m_BP->index ) << _T( " " ) << F( _T( "%d" ), ( int ) m_BP->ignoreCount );
          m_pDriver->QueueCommand( new DebuggerCmd( m_pDriver, cmd ), DebuggerDriver::High );
        }
      } else if( rePendingBreakpoint.Matches( output ) ) {
        if( !m_BP->func.IsEmpty() ) {
          m_pDriver->Log( _T( "GDB workaround for constructor/destructor breakpoints activated." ) );
        }
        rePendingBreakpoint.GetMatch( output, 1 ).ToLong( &m_BP->index );
        if( m_BP->useCondition && !m_BP->condition.IsEmpty() ) {
          m_BP->wantsCondition = true;
        }
        if( m_BP->useIgnoreCount && m_BP->ignoreCount > 0 ) {
          wxString cmd;
          cmd << _T( "ignore " ) << F( _T( "%d" ), ( int ) m_BP->index ) << _T( " " ) << F( _T( "%d" ), ( int ) m_BP->ignoreCount );
          m_pDriver->QueueCommand( new DebuggerCmd( m_pDriver, cmd ), DebuggerDriver::High );
        }
      } else if( reDataBreakpoint.Matches( output ) ) {
        reDataBreakpoint.GetMatch( output, 1 ).ToLong( &m_BP->index );
      } else if( reHWBreakpoint.Matches( output ) ) {
        reHWBreakpoint.GetMatch( output, 1 ).ToLong( &m_BP->index );
        reHWBreakpoint.GetMatch( output, 2 ).ToULong( &m_BP->address, 16 );
      } else if( reTemporaryBreakpoint.Matches( output ) ) {
        reTemporaryBreakpoint.GetMatch( output, 1 ).ToLong( &m_BP->index );
      } else
      { m_pDriver->Log( output ); }
      Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
    }
};


class GdbCmd_AddDataBreakpoint : public DebuggerCmd {
    cb::shared_ptr<DebuggerBreakpoint> m_BP;
  public:

    GdbCmd_AddDataBreakpoint( DebuggerDriver* driver, cb::shared_ptr<DebuggerBreakpoint> bp )
      : DebuggerCmd( driver ),
        m_BP( bp ) {
      if( m_BP->enabled ) {
        m_Cmd << _T( "output &" ) << m_BP->breakAddress;
      }
    }
    void ParseOutput( const wxString& output ) {
      if( output.StartsWith( _T( "No symbol " ) ) || output.StartsWith( _T( "Attempt to " ) ) ) {
        m_pDriver->Log( output );
      } else {
        if( reGenericHexAddress.Matches( output ) ) {
          wxString contents = reGenericHexAddress.GetMatch( output, 1 );
          m_BP->breakAddress = _T( "*" ) + contents;
          DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
          dbgManager->GetBreakpointDialog()->Reload();
          m_pDriver->QueueCommand( new GdbCmd_AddBreakpoint( m_pDriver, m_BP ), DebuggerDriver::High );
        }
      }
    }
};

class GdbCmd_RemoveBreakpoint : public DebuggerCmd {
  public:
    GdbCmd_RemoveBreakpoint( DebuggerDriver* driver, cb::shared_ptr<DebuggerBreakpoint> bp )
      : DebuggerCmd( driver ),
        m_BP( bp ) {
      if( !bp ) {
        m_Cmd << _T( "delete breakpoints" );
        return;
      }
      if( bp->index >= 0 ) {
        m_Cmd << _T( "delete breakpoints " ) << F( _T( "%d" ), ( int ) bp->index );
      }
    }
    void ParseOutput( const wxString& output ) {
      if( !m_BP ) {
        return;
      }
      if( !output.IsEmpty() ) {
        m_pDriver->Log( output );
      }
    }

    cb::shared_ptr<DebuggerBreakpoint> m_BP;
};

class GdbCmd_SetCatch : public DebuggerCmd {
    wxString m_type;
    int *m_resultIndex;
    wxRegEx m_regExp;
  public:
    GdbCmd_SetCatch( DebuggerDriver *driver, const wxString &type, int *resultIndex ) :
      DebuggerCmd( driver ),
      m_type( type ),
      m_resultIndex( resultIndex ),
      m_regExp( _T( "^Catchpoint[ \\t]([0-9]+)[ \\t]\\(" ) + type + _T( "\\)$" ), wxRE_ADVANCED ) {
      m_Cmd = _T( "catch " ) + type;
    }
    void ParseOutput( const wxString& output ) {
      if( m_regExp.Matches( output ) ) {
        long index;
        m_regExp.GetMatch( output, 1 ).ToLong( &index );
        *m_resultIndex = index;
      }
    }
};

class GdbCmd_Continue : public DebuggerContinueBaseCmd {
  public:
    GdbCmd_Continue( DebuggerDriver* driver ) :
      DebuggerContinueBaseCmd( driver, _T( "cont" ) ) {
    }
    virtual void Action() { m_pDriver->NotifyDebuggeeContinued(); }
};

class GdbCmd_Start : public DebuggerContinueBaseCmd {
  public:
    GdbCmd_Start( DebuggerDriver* driver, const wxString &cmd ) :
      DebuggerContinueBaseCmd( driver, cmd ) {
    }

    virtual void ParseOutput( const wxString &output ) {
      const wxArrayString &lines = GetArrayFromString( output, _T( '\n' ) );
      for( size_t ii = 0; ii < lines.GetCount(); ++ii ) {
        if( lines[ii].StartsWith( _T( "No symbol table loaded" ) )
            || lines[ii].StartsWith( _T( "No executable file specified" ) )
            || lines[ii].StartsWith( _T( "No executable specified" ) )
            || lines[ii].StartsWith( _T( "Don't know how to run" ) ) ) {
          m_pDriver->Log( _T( "Starting the debuggee failed: " ) + lines[ii] );
          m_pDriver->MarkProgramStopped( true );
          m_pDriver->QueueCommand( new DebuggerCmd( m_pDriver, _T( "quit" ) ) );
        }
      }
    }
};


class GdbCmd_InfoProgram : public DebuggerCmd {
  public:
    GdbCmd_InfoProgram( DebuggerDriver* driver )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "info program" );
    }
    void ParseOutput( const wxString& output ) {
      wxString pid_str;
      if( reInfoProgramThread.Matches( output ) ) {
        pid_str = reInfoProgramThread.GetMatch( output, 1 );
      } else if( reInfoProgramProcess.Matches( output ) ) {
        pid_str = reInfoProgramProcess.GetMatch( output, 1 );
      }
      if( !pid_str.IsEmpty() ) {
        long pid;
        if( pid_str.ToLong( &pid, 10 ) && pid != 0 ) {
          m_pDriver->SetChildPID( pid );
        }
      }
    }
};


class GdbCmd_Threads : public DebuggerCmd {
  public:
    GdbCmd_Threads( DebuggerDriver* driver )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "info threads" );
    }
    void ParseOutput( const wxString& output ) {
      m_pDriver->GetThreads().clear();
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        if( reInfoThreads.Matches( lines[i] ) ) {
          wxString active = reInfoThreads.GetMatch( lines[i], 1 );
          active.Trim( true );
          active.Trim( false );
          wxString num = reInfoThreads.GetMatch( lines[i], 2 );
          wxString info = reInfoThreads.GetMatch( lines[i], 3 );
          long number;
          num.ToLong( &number, 10 );
          DebuggerDriver::ThreadsContainer &threads = m_pDriver->GetThreads();
          threads.push_back( cb::shared_ptr<cbThread>( new cbThread( !active.empty(), number, info ) ) );
        }
      }
      Manager::Get()->GetDebuggerManager()->GetThreadsDialog()->Reload();
    }
};


class GdbCmd_Watch : public DebuggerCmd {
    cb::shared_ptr<GDBWatch> m_watch;
  public:
    GdbCmd_Watch( DebuggerDriver* driver, cb::shared_ptr<GDBWatch> watch ) :
      DebuggerCmd( driver ),
      m_watch( watch ) {
      wxString type;
      wxString symbol;
      m_watch->GetSymbol( symbol );
      m_watch->GetType( type );
      type.Trim( true );
      type.Trim( false );
      m_Cmd << _T( "output " );
      switch( m_watch->GetFormat() ) {
        case Decimal:
          m_Cmd << _T( "/d " );
          break;
        case Unsigned:
          m_Cmd << _T( "/u " );
          break;
        case Hex:
          m_Cmd << _T( "/x " );
          break;
        case Binary:
          m_Cmd << _T( "/t " );
          break;
        case Char:
          m_Cmd << _T( "/c " );
          break;
        case Float:
          m_Cmd << _T( "/f " );
          break;
        case Last:
        case Any:
        case Undefined:
        default:
          break;
      }
      if( !m_watch->IsArray() &&  m_watch->GetFormat() == Undefined && type.Contains( _T( '[' ) ) ) {
        m_watch->SetArray( true );
      }
      if( m_watch->IsArray() && m_watch->GetArrayCount() > 0 ) {
        m_Cmd << _T( "(" ) << symbol << _T( ")" );
        m_Cmd << F( _T( "[%d]@%d" ), m_watch->GetArrayStart(), m_watch->GetArrayCount() );
      } else
      { m_Cmd << symbol; }
    }
    void ParseOutput( const wxString& output ) {
      wxString w = output;
      w.Trim( true );
      w.Trim( false );
      if( !ParseGDBWatchValue( m_watch, w ) ) {
        wxString symbol;
        m_watch->GetSymbol( symbol );
        wxString const &msg = _T( "Parsing GDB output failed for '" ) + symbol + _T( "'!" );
        m_watch->SetValue( msg );
        Manager::Get()->GetLogManager()->LogError( msg );
      }
    }
};


class GdbCmd_FindWatchType : public DebuggerCmd {
    cb::shared_ptr<GDBWatch> m_watch;
    bool m_firstTry;
  public:
    GdbCmd_FindWatchType( DebuggerDriver* driver, cb::shared_ptr<GDBWatch> watch, bool firstTry = true ) :
      DebuggerCmd( driver ),
      m_watch( watch ),
      m_firstTry( firstTry ) {
      if( m_firstTry ) {
        m_Cmd << _T( "whatis " );
      } else
      { m_Cmd << _T( "whatis &" ); }
      wxString symbol;
      m_watch->GetSymbol( symbol );
      m_Cmd << symbol;
    }
    void ParseOutput( const wxString& output ) {
      if( m_firstTry && output == _T( "Attempt to take contents of a non-pointer value." ) ) {
        m_pDriver->QueueCommand( new GdbCmd_FindWatchType( m_pDriver, m_watch, false ), DebuggerDriver::High );
        return;
      }
      if( output.StartsWith( _T( "No symbol \"" ) ) && output.EndsWith( _T( "\" in current context." ) ) ) {
        m_watch->RemoveChildren();
        m_watch->SetType( wxEmptyString );
        m_watch->SetValue( _T( "Not available in current context!" ) );
        return;
      }
      wxString tmp = output.AfterFirst( _T( '=' ) );
      if( !m_firstTry && !tmp.empty() ) {
        tmp = tmp.substr( 0, tmp.length() - 1 );
      }
      wxString old_type;
      m_watch->GetType( old_type );
      if( old_type != tmp ) {
        m_watch->RemoveChildren();
        m_watch->SetType( tmp );
        m_watch->SetValue( wxEmptyString );
      }
      m_pDriver->QueueCommand( new GdbCmd_Watch( m_pDriver, m_watch ), DebuggerDriver::High );
    }
};


class GdbCmd_TooltipEvaluation : public DebuggerCmd {
    wxRect m_WinRect;
    wxString m_What;
    wxString m_Type;
    wxString m_Address;
    bool m_autoDereferenced;
  public:
    GdbCmd_TooltipEvaluation( DebuggerDriver* driver, const wxString& what, const wxRect& tiprect,
                              const wxString& w_type = wxEmptyString, const wxString& address = wxEmptyString )
      : DebuggerCmd( driver ),
        m_WinRect( tiprect ),
        m_What( what ),
        m_Type( w_type ),
        m_Address( address ),
        m_autoDereferenced( false ) {
      m_Type.Trim( true );
      m_Type.Trim( false );
      if( IsPointerType( w_type ) ) {
        m_What = _T( "*" ) + m_What;
        m_autoDereferenced = true;
      }
      m_Cmd << _T( "output " );
      m_Cmd << m_What;
    }
    void ParseOutput( const wxString& output ) {
      wxString contents = output;
      contents.Trim( true );
      contents.Trim( false );
      cb::shared_ptr<GDBWatch> watch( new GDBWatch( m_What ) );
      watch->SetType( m_Type );
      ParseGDBWatchValue( watch, contents );
      if( !m_Address.empty() && m_autoDereferenced ) {
        wxString value;
        watch->GetValue( value );
        if( value.empty() ) {
          watch->SetValue( m_Address );
        } else if( !value.Contains( m_Address ) ) {
          watch->SetValue( m_Address + _T( ": " ) + value );
        }
      }
      watch->SetForTooltip( true );
      if( watch->GetChildCount() > 0 ) {
        watch->Expand( true );
      }
      if( Manager::Get()->GetDebuggerManager()->ShowValueTooltip( watch, m_WinRect ) ) {
        m_pDriver->GetDebugger()->AddWatchNoUpdate( watch );
      }
    }
};


class GdbCmd_FindTooltipAddress : public DebuggerCmd {
    wxRect m_WinRect;
    wxString m_What;
    wxString m_Type;
  public:
    GdbCmd_FindTooltipAddress( DebuggerDriver* driver, const wxString& what, const wxRect& tiprect, const wxString& w_type = wxEmptyString )
      : DebuggerCmd( driver ),
        m_WinRect( tiprect ),
        m_What( what ),
        m_Type( w_type ) {
      if( m_Type.IsEmpty() ) {
        m_pDriver->QueueCommand( new GdbCmd_TooltipEvaluation( m_pDriver, m_What, m_WinRect, m_Type ), DebuggerDriver::High );
        return;
      }
      m_Cmd << _T( "output " );
      if( m_Type.Last() != _T( '*' ) ) {
        m_Cmd << _T( '&' );
      }
      m_Cmd << m_What;
    }
    void ParseOutput( const wxString& output ) {
      wxString tmp;
      if( reGenericHexAddress.Matches( output ) ) {
        tmp = reGenericHexAddress.GetMatch( output, 1 );
      }
      m_pDriver->QueueCommand( new GdbCmd_TooltipEvaluation( m_pDriver, m_What, m_WinRect, m_Type, tmp ), DebuggerDriver::High );
    }
};

class GdbCmd_FindTooltipType : public DebuggerCmd {
    wxRect m_WinRect;
    wxString m_What;
    static bool singleUsage;
  public:
    GdbCmd_FindTooltipType( DebuggerDriver* driver, const wxString& what, const wxRect& tiprect )
      : DebuggerCmd( driver ),
        m_WinRect( tiprect ),
        m_What( what ) {
      if( !singleUsage ) {
        singleUsage = true;
        m_Cmd << _T( "whatis " );
        m_Cmd << m_What;
      }
    }
    ~GdbCmd_FindTooltipType() {
      singleUsage = false;
    }
    void ParseOutput( const wxString& output ) {
      wxString tmp = output.AfterFirst( _T( '=' ) );
      tmp.Trim( false );
      m_pDriver->QueueCommand( new GdbCmd_FindTooltipAddress( m_pDriver, m_What, m_WinRect, tmp ), DebuggerDriver::High );
    }
};
bool GdbCmd_FindTooltipType::singleUsage = false;

class GdbCmd_LocalsFuncArgs : public DebuggerCmd {
    cb::shared_ptr<GDBWatch> m_watch;
    bool m_doLocals;
  public:
    GdbCmd_LocalsFuncArgs( DebuggerDriver* driver, cb::shared_ptr<GDBWatch> watch, bool doLocals ) :
      DebuggerCmd( driver ),
      m_watch( watch ),
      m_doLocals( doLocals ) {
      if( m_doLocals ) {
        m_Cmd = _T( "info locals" );
      } else
      { m_Cmd = _T( "info args" ); }
    }
    void ParseOutput( const wxString& output ) {
      if( ( m_doLocals && output == _T( "No locals." ) ) || ( !m_doLocals && output == _T( "No arguments." ) ) ) {
        m_watch->RemoveChildren();
        return;
      }
      std::vector<GDBLocalVariable> watchStrings;
      TokenizeGDBLocals( watchStrings, output );
      m_watch->MarkChildsAsRemoved();
      for( std::vector<GDBLocalVariable>::const_iterator it = watchStrings.begin(); it != watchStrings.end(); ++it ) {
        if( it->error ) {
          continue;
        }
        cb::shared_ptr<GDBWatch> watch = AddChild( m_watch, it->name );
        ParseGDBWatchValue( watch, it->value );
      }
      m_watch->RemoveMarkedChildren();
    }
};


class GdbCmd_ChangeFrame : public DebuggerCmd {
    int   m_addrchgmode;
  public:
    int AddrChgMode() { return m_addrchgmode; }
    GdbCmd_ChangeFrame( DebuggerDriver* driver, int frameno, int p_addrchgmode = 1 )
      : DebuggerCmd( driver )
      , m_addrchgmode( p_addrchgmode ) {
      m_Cmd << _T( "frame " ) << frameno;
    }
    void ParseOutput( const wxString& output ) {
      m_pDriver->Log( output );
    }
};

class GdbCmd_Backtrace : public DebuggerCmd {
  public:
    GdbCmd_Backtrace( DebuggerDriver* driver )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "bt 30" );
    }
    void ParseOutput( const wxString& output ) {
      int validFrameNumber = -1;
      cbStackFrame validSF;
      m_pDriver->GetStackFrames().clear();
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        cbStackFrame sf;
        bool hasLineInfo;
        bool matched = MatchLine( sf, hasLineInfo, lines[i] );
        if( matched ) {
          if( hasLineInfo && validFrameNumber == -1 ) {
            validSF = sf;
            validFrameNumber = sf.GetNumber();
          }
          m_pDriver->GetStackFrames().push_back( cb::shared_ptr<cbStackFrame>( new cbStackFrame( sf ) ) );
        }
      }
      if( validFrameNumber > 0 ) {
        bool autoSwitch = M_Code_DebuggerCommonConfig::GetFlag( M_Code_DebuggerCommonConfig::AutoSwitchFrame );
        if( !autoSwitch ) {
          long line;
          if( !m_pDriver->GetStackFrames().empty() ) {
            if( m_pDriver->GetUserSelectedFrame() != -1 ) {
              validFrameNumber = m_pDriver->GetUserSelectedFrame();
              DebuggerDriver::StackFrameContainer const &frames = m_pDriver->GetStackFrames();
              if( validFrameNumber >= 0 && validFrameNumber <= static_cast<int>( frames.size() ) )
              { validSF = *frames[validFrameNumber]; }
              else if( !frames.empty() )
              { validSF = *frames.front(); }
            }
          }
          if( validSF.GetLine().ToLong( &line ) ) {
            m_pDriver->Log( F( _T( "Displaying first frame with valid source info (#%d)" ), validFrameNumber ) );
            m_pDriver->ShowFile( validSF.GetFilename(), line );
          }
        } else {
          if( m_pDriver->GetUserSelectedFrame() != -1 ) {
            validFrameNumber = m_pDriver->GetUserSelectedFrame();
          }
          m_pDriver->QueueCommand( new GdbCmd_ChangeFrame( m_pDriver, validFrameNumber ) );
          m_pDriver->SetCurrentFrame( validFrameNumber, false );
        }
      }
      Manager::Get()->GetDebuggerManager()->GetBacktraceDialog()->Reload();
    }

    static bool MatchLine( cbStackFrame &sf, bool &hasLineInfo, const wxString &line ) {
      hasLineInfo = false;
      if( reBTX.Matches( line ) ) {
        long int number;
        reBTX.GetMatch( line, 1 ).ToLong( &number );
        sf.SetNumber( number );
        sf.SetAddress( cbDebuggerStringToAddress( reBTX.GetMatch( line, 2 ) ) );
        sf.SetSymbol( reBTX.GetMatch( line, 3 ) + reBTX.GetMatch( line, 4 ) );
      } else if( reBT1.Matches( line ) ) {
        long int number;
        reBT1.GetMatch( line, 1 ).ToLong( &number );
        sf.SetNumber( number );
        sf.SetAddress( cbDebuggerStringToAddress( reBT1.GetMatch( line, 2 ) ) );
        sf.SetSymbol( reBT1.GetMatch( line, 3 ) + reBT1.GetMatch( line, 4 ) );
      } else if( reBT0.Matches( line ) ) {
        long int number;
        reBT0.GetMatch( line, 1 ).ToLong( &number );
        sf.SetNumber( number );
        sf.SetAddress( 0 );
        sf.SetSymbol( reBT0.GetMatch( line, 2 ) );
        sf.SetFile( reBT0.GetMatch( line, 3 ), wxEmptyString );
      } else if( reBT4.Matches( line ) ) {
        long int number;
        reBT4.GetMatch( line, 1 ).ToLong( &number );
        sf.SetNumber( number );
        sf.SetAddress( cbDebuggerStringToAddress( reBT4.GetMatch( line, 2 ) ) );
        sf.SetSymbol( reBT4.GetMatch( line, 3 ) );
      } else
      { return false; }
      sf.MakeValid( true );
      if( reBT2.Matches( line ) ) {
        sf.SetFile( reBT2.GetMatch( line, 1 ), reBT2.GetMatch( line, 2 ) );
        hasLineInfo = true;
      } else if( reBT3.Matches( line ) ) {
        sf.SetFile( reBT3.GetMatch( line, 1 ), wxEmptyString );
      }
      return true;
    }
};

class GdbCmd_InfoRegisters : public DebuggerCmd {
    wxString m_disassemblyFlavor;
  public:
    GdbCmd_InfoRegisters( DebuggerDriver* driver, wxString disassemblyFlavor = wxEmptyString ) :
      DebuggerCmd( driver ),
      m_disassemblyFlavor( disassemblyFlavor ) {
      m_Cmd << _T( "info registers" );
    };

    void ParseOutput( const wxString& output ) {
      if( m_disassemblyFlavor == _T( "set disassembly-flavor or32" ) ) {
        ParseOutputFromOR32gdbPort( output );
      } else {
        cbCPURegistersDlg *dialog = Manager::Get()->GetDebuggerManager()->GetCPURegistersDialog();
        wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
        for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
          if( reRegisters.Matches( lines[i] ) ) {
            const wxString &addr = reRegisters.GetMatch( lines[i], 1 );
            const wxString &hex = reRegisters.GetMatch( lines[i], 2 );
            const wxString &interpreted = reRegisters.GetMatch( lines[i], 3 );
            dialog->SetRegisterValue( addr, hex, interpreted );
          }
        }
      }
    }

    void ParseOutputFromOR32gdbPort( const wxString& output ) {
      wxArrayString lines = GetArrayFromString( output, _T( "\n" ) );
      if( ( output == _T( "" ) ) || ( lines.GetCount() < 2 ) ) {
        return;
      }
      cbCPURegistersDlg *dialog = Manager::Get()->GetDebuggerManager()->GetCPURegistersDialog();
      for( unsigned int i = 0; i < lines.GetCount(); i += 2 ) {
        wxArrayString regMnemonics;
        wxArrayString regValues;
        wxString RegisterMnemonicString;
        wxString RegisterValueString;
        RegisterValueString = lines.Item( i + 1 );
        wxStringTokenizer RegisterValueStringTokenizer( ( RegisterValueString ), _T( " " ), wxTOKEN_STRTOK );
        while( RegisterValueStringTokenizer.HasMoreTokens() ) {
          wxString RegisterValueStringToken = RegisterValueStringTokenizer.GetNextToken();
          regValues.Add( RegisterValueStringToken );
        }
        RegisterMnemonicString = lines.Item( i );
        wxStringTokenizer RegisterMnemonicStringTokenizer( ( RegisterMnemonicString ), _T( " " ), wxTOKEN_STRTOK );
        while( RegisterMnemonicStringTokenizer.HasMoreTokens() ) {
          wxString RegisterMnemonicStringToken = RegisterMnemonicStringTokenizer.GetNextToken();
          regMnemonics.Add( RegisterMnemonicStringToken );
        }
        for( unsigned int j = 0; j < regMnemonics.GetCount(); j++ ) {
          wxString reg = regMnemonics.Item( j );
          wxString addr = regValues.Item( j );
          if( !reg.IsEmpty() && !addr.IsEmpty() ) {
            dialog->SetRegisterValue( reg, addr, wxEmptyString );
          }
        }
      }
    }
};

class GdbCmd_Disassembly : public DebuggerCmd {
    bool m_mixedMode;
  public:
    GdbCmd_Disassembly( DebuggerDriver* driver, bool MixedMode, wxString hexAddrStr )
      : DebuggerCmd( driver )
      , m_mixedMode( MixedMode ) {
      m_Cmd << _T( "disassemble" );
      if( m_mixedMode ) {
        m_Cmd << _T( " /m" );
      }
      if( hexAddrStr.IsEmpty() ) {
        m_Cmd << _T( " $pc" );
      } else if( _T( "0x" ) == hexAddrStr.Left( 2 ) || _T( "0X" ) == hexAddrStr.Left( 2 ) ) {
        m_Cmd << _T( " " ) << hexAddrStr;
      } else
      { m_Cmd << _T( " 0x" ) << hexAddrStr; }
    }
    void ParseOutput( const wxString& output ) {
      const wxString disasmerror( _T( "No function contains specified address." ) );
      cbDisassemblyDlg *dialog = Manager::Get()->GetDebuggerManager()->GetDisassemblyDialog();
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        if( lines[i].StartsWith( disasmerror ) ) {
          dialog->AddSourceLine( 0, disasmerror );
          break ;
        } else if( reDisassembly.Matches( lines[i] ) ) {
          uint64_t addr = cbDebuggerStringToAddress( reDisassembly.GetMatch( lines[i], 1 ) );
          dialog->AddAssemblerLine( addr, reDisassembly.GetMatch( lines[i], 2 ) );
        } else if( m_mixedMode && reDisassemblySource.Matches( lines[i] ) ) {
          long int lineno;
          reDisassemblySource.GetMatch( lines[i], 1 ).ToLong( &lineno, 10 );
          dialog->AddSourceLine( lineno, reDisassemblySource.GetMatch( lines[i], 2 ) );
        }
      }
      dialog->CenterCurrentLine();
    }
};

class GdbCmd_DisassemblyInit : public DebuggerCmd {
    wxString m_disassemblyFlavor;
    wxString m_hexAddrStr;
    static wxString LastAddr;
    static wxString LastSymbol;
  public:
    GdbCmd_DisassemblyInit( DebuggerDriver* driver, wxString disassemblyFlavor = wxEmptyString,
                            wxString hexAddrStr = _T( "" ) )
      : DebuggerCmd( driver ),
        m_disassemblyFlavor( disassemblyFlavor ),
        m_hexAddrStr( hexAddrStr ) {
      m_Cmd << _T( "if 1\n" ) ;
      if( m_hexAddrStr.empty() ) {
        const Cursor &cursor = driver->GetCursor() ;
        if( cursor.address.empty() ) {
          m_Cmd << _T( "disassemble $pc,$pc+50\n" ) ;
        } else
        { m_Cmd << _T( "disassemble " ) << cursor.address << _T( "\n" ) ; }
      } else
      { m_Cmd << _T( "disassemble " ) << m_hexAddrStr << _T( "\n" ) ; }
      m_Cmd << _T( "info frame\n" ) << _T( "end" );
    };

    void ParseOutput( const wxString& p_output ) {
      cbDisassemblyDlg *dialog = Manager::Get()->GetDebuggerManager()->GetDisassemblyDialog();
      wxString frame_output, reg_output ;
      size_t apos ;
      apos = p_output.find( _T( "Stack level " ) );
      if( apos == wxString::npos ) {
        m_pDriver->Log( _T( "Failure finding \"Stack level \"" ) );
        apos = p_output.length();
      }
      reg_output = p_output.substr( 0, apos );
      frame_output = p_output.substr( apos, p_output.length() - apos );
      wxString &output = frame_output ;
      if( reDisassemblyCurPC.Matches( reg_output ) ) {
        if( m_hexAddrStr.empty() ) {
          m_hexAddrStr = reDisassemblyCurPC.GetMatch( reg_output, 1 );
        }
      } else
      { m_pDriver->Log( _T( "Failure matching reg_output" ) ); }
      const wxArrayString &lines = GetArrayFromString( output, _T( '\n' ) );
      if( lines.Count() <= 2 ) {
        return;
      }
      size_t firstLine = 0;
      for( ; firstLine < lines.Count() && !reDisassemblyInit.Matches( lines[firstLine] ); ++firstLine )
        ;
      if( firstLine + 1 < lines.Count() ) {
        bool sameSymbol = false;
        if( reDisassemblyInitSymbol.Matches( lines[firstLine] ) ) {
          const wxString &symbol = reDisassemblyInitSymbol.GetMatch( lines[firstLine], 1 )
                                   + reDisassemblyInitSymbol.GetMatch( lines[firstLine], 2 );
          sameSymbol = ( LastSymbol == symbol );
          if( !sameSymbol ) {
            LastSymbol = symbol;
          }
        }
        cbStackFrame sf;
        const wxString &addr = reDisassemblyInit.GetMatch( output, 1 );
        if( addr == LastAddr && sameSymbol ) {
          return;
        }
        LastAddr = addr;
        sf.SetAddress( cbDebuggerStringToAddress( addr ) );
        if( reDisassemblyInitFunc.Matches( output ) ) {
          sf.SetSymbol( reDisassemblyInitFunc.GetMatch( output, 2 ) );
        }
        sf.MakeValid( true );
        dialog->Clear( sf );
        if( !m_hexAddrStr.empty() ) {
          dialog->SetActiveAddress( cbDebuggerStringToAddress( m_hexAddrStr ) );
          Cursor acursor = m_pDriver->GetCursor();
          acursor.address = m_hexAddrStr;
          m_pDriver->SetCursor( acursor );
        }
        bool mixedmode = Manager::Get()->GetDebuggerManager()->IsDisassemblyMixedMode();
        m_pDriver->QueueCommand( new GdbCmd_Disassembly( m_pDriver, mixedmode, m_hexAddrStr ) );
      }
    }

    static void Clear() {
      LastAddr.Clear();
      LastSymbol.Clear();
    }
};

wxString GdbCmd_DisassemblyInit::LastAddr;
wxString GdbCmd_DisassemblyInit::LastSymbol;
class GdbCmd_ExamineMemory : public DebuggerCmd {
  public:

    GdbCmd_ExamineMemory( DebuggerDriver* driver )
      : DebuggerCmd( driver ) {
      cbExamineMemoryDlg *dialog = Manager::Get()->GetDebuggerManager()->GetExamineMemoryDialog();
      const wxString &address = CleanStringValue( dialog->GetBaseAddress() );
      m_Cmd.Printf( _T( "x/%dxb %s" ), dialog->GetBytes(), address.c_str() );
    }

    void ParseOutput( const wxString& output ) {
      cbExamineMemoryDlg *dialog = Manager::Get()->GetDebuggerManager()->GetExamineMemoryDialog();
      dialog->Begin();
      dialog->Clear();
      wxArrayString lines = GetArrayFromString( output, _T( '\n' ) );
      wxString addr, memory;
      for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
        if( reExamineMemoryLine.Matches( lines[i] ) ) {
          addr = reExamineMemoryLine.GetMatch( lines[i], 1 );
          memory = reExamineMemoryLine.GetMatch( lines[i], 2 );
        } else {
          if( lines[i].First( _T( ':' ) ) == -1 ) {
            dialog->AddError( lines[i] );
            continue;
          }
          addr = lines[i].BeforeFirst( _T( ':' ) );
          memory = lines[i].AfterFirst( _T( ':' ) );
        }
        size_t pos = memory.find( _T( 'x' ) );
        while( pos != wxString::npos ) {
          wxString hexbyte;
          hexbyte << memory[pos + 1];
          hexbyte << memory[pos + 2];
          dialog->AddHexByte( addr, hexbyte );
          pos = memory.find( _T( 'x' ), pos + 1 );
        }
      }
      dialog->End();
    }
};

class GdbCmd_RemoteBaud : public DebuggerCmd {
  public:
    GdbCmd_RemoteBaud( DebuggerDriver* driver, const wxString& baud )
      : DebuggerCmd( driver ) {
      m_Cmd << _T( "set remotebaud " ) << baud;
      driver->Log( _T( "Setting serial connection speed to " ) + baud );
    }
    void ParseOutput( M_Code_unused const wxString& output ) {
    }
};

class GdbCmd_RemoteTarget : public DebuggerCmd {
  public:
    GdbCmd_RemoteTarget( DebuggerDriver* driver, RemoteDebugging* rd )
      : DebuggerCmd( driver ) {
      const wxString targetRemote = rd->extendedRemote ? _T( "target extended-remote " ) : _T( "target remote " );
      switch( rd->connType ) {
        case RemoteDebugging::TCP: {
          if( !rd->ip.IsEmpty() && !rd->ipPort.IsEmpty() ) {
            m_Cmd << targetRemote << _T( "tcp:" ) << rd->ip << _T( ":" ) << rd->ipPort;
          }
        }
        break;
        case RemoteDebugging::UDP: {
          if( !rd->ip.IsEmpty() && !rd->ipPort.IsEmpty() ) {
            m_Cmd << targetRemote << _T( "udp:" ) << rd->ip << _T( ":" ) << rd->ipPort;
          }
        }
        break;
        case RemoteDebugging::Serial: {
          if( !rd->serialPort.IsEmpty() ) {
            m_Cmd << targetRemote << rd->serialPort;
          }
        }
        break;
        default:
          break;
      }
      Manager::Get()->GetMacrosManager()->ReplaceEnvVars( m_Cmd );
      if( !m_Cmd.IsEmpty() ) {
        driver->Log( _T( "Connecting to remote target" ) );
      } else
      { m_pDriver->Log( _T( "Invalid settings for remote debugging!" ) ); }
    }
    void ParseOutput( const wxString& output ) {
      wxString errMsg;
      if( output.Contains( _T( "No route to host" ) ) ) {
        errMsg << _T( "Can't connect to the remote system.\nVerify your connection settings and that\nthe remote system is reachable/powered-on." );
      } else if( output.Contains( _T( "Connection refused" ) ) ) {
        errMsg << _T( "Connection refused by the remote system.\nVerify your connection settings and that\nthe GDB server/proxy is running on the remote system." );
      } else if( output.Contains( _T( "Malformed response" ) ) ||
                 output.Contains( _T( "packet error" ) ) ) {
        errMsg << _T( "Connection can't be established.\nVerify your connection settings and that\nthe GDB server/proxy is running on the remote system." );
      } else if( output.Contains( _T( "Invalid argument" ) ) ) {
        errMsg << _T( "Invalid argument.\nVerify your connection settings (probably some typo)." );
      } else if( output.Contains( _T( "unknown host" ) ) ) {
        errMsg << _T( "Unknown host.\nVerify your connection settings (probably some typo)." );
      }
      if( !errMsg.IsEmpty() ) {
        m_pDriver->Log( _T( "Failed" ) );
        errMsg << _T( "\nThe exact error message was:\n\n" );
        errMsg << output;
        InfoWindow::Tip( errMsg, _T( "错误" ), 10000, 1000 );
        return;
      }
      m_pDriver->Log( _T( "Connected" ) );
    }
};
class GdbCmd_StepOrNextInstruction : public DebuggerContinueBaseCmd {
  public:
    GdbCmd_StepOrNextInstruction( GDB_driver* driver, const wxChar *command )
      : DebuggerContinueBaseCmd( driver ) {
      m_Cmd << command;
    }
    void ParseOutput( const wxString& output ) {
      DebuggerManager *manager = Manager::Get()->GetDebuggerManager();
      if( !manager->UpdateDisassembly() ) {
        return;
      }
      wxString disasm_flavour = static_cast<GDB_driver*>( m_pDriver )->AsmFlavour() ;
      cbDisassemblyDlg *dialog = manager->GetDisassemblyDialog();
      m_pDriver->Log( output );
      wxString addrstr;
      if( reStepI.Matches( output ) ) {
        addrstr = reStepI.GetMatch( output, 6 );
      } else if( reStepI2.Matches( output ) ) {
        addrstr = reStepI2.GetMatch( output, 1 );
      } else if( reStepI3.Matches( output ) ) {
        addrstr = reStepI3.GetMatch( output, 1 );
      } else if( reStepI4.Matches( output ) ) {
        addrstr = reStepI4.GetMatch( output, 1 );
      } else {
        cbStackFrame sf;
        dialog->Clear( sf );
        m_pDriver->QueueCommand( new GdbCmd_DisassemblyInit( m_pDriver, disasm_flavour ) );
        return;
      }
      if( addrstr.empty() ) {
        return;
      }
      if( !dialog->SetActiveAddress( cbDebuggerStringToAddress( addrstr ) ) ) {
        m_pDriver->QueueCommand( new GdbCmd_DisassemblyInit( m_pDriver, disasm_flavour, addrstr ) );
      }
    }
};
class GdbCmd_StepInstruction : public GdbCmd_StepOrNextInstruction {
  public:
    GdbCmd_StepInstruction( GDB_driver* driver )
      : GdbCmd_StepOrNextInstruction( driver, _T( "nexti" ) ) {
    }
};
class GdbCmd_StepIntoInstruction : public GdbCmd_StepOrNextInstruction {
  public:
    GdbCmd_StepIntoInstruction( GDB_driver* driver )
      : GdbCmd_StepOrNextInstruction( driver, _T( "stepi" ) ) {
    }
};
class GdbCmd_FindCursor : public DebuggerCmd {
  public:
    GdbCmd_FindCursor( GDB_driver *driver ) :
      DebuggerCmd( driver, _T( "info frame" ) ) {
    }

    void ParseOutput( const wxString& output ) {
      const wxArrayString &lines = GetArrayFromString( output, _T( '\n' ) );
      if( lines.Count() <= 2 ) {
        return;
      }
      size_t firstLine = 0;
      for( ; firstLine < lines.Count() && !reDisassemblyInit.Matches( lines[firstLine] ); ++firstLine )
        ;
      firstLine++;
      if( firstLine < lines.Count() ) {
        wxString symbol, file, line;
        if( reDisassemblyInitSymbol.Matches( lines[firstLine] ) ) {
          symbol = reDisassemblyInitSymbol.GetMatch( lines[firstLine], 1 );
          file = reDisassemblyInitSymbol.GetMatch( lines[firstLine], 2 );
          line = reDisassemblyInitSymbol.GetMatch( lines[firstLine], 3 );
        }
        const wxString &addr = reDisassemblyInit.GetMatch( output, 1 );
        long longAddress;
        addr.ToULong( ( unsigned long int* )&longAddress, 16 );
        Cursor cursor = m_pDriver->GetCursor();
        cursor.address =  addr;
        cursor.changed = true;
        cursor.file = file;
        cursor.function = symbol;
        if( !line.ToLong( &cursor.line ) ) {
          cursor.line = -1;
        }
        m_pDriver->SetCursor( cursor );
        m_pDriver->NotifyCursorChanged();
      }
    }
};
#endif
